package me.zy.spring.framework.webmvc;

import me.zy.spring.framework.annotation.ZYController;
import me.zy.spring.framework.annotation.ZYRequestMapping;
import me.zy.spring.framework.context.ZYApplicationContext;
import me.zy.spring.framework.webmvc.servlet.ZYHandlerAdapter;

import javax.servlet.ServletConfig;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.File;
import java.io.IOException;
import java.lang.reflect.Method;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.regex.Matcher;
import java.util.regex.Pattern;

public class ZYDispatcherServlet extends HttpServlet{
    //web.xml中初始化此参数
    private final String CONTEXT_CONFIG_LOCATION = "contextConfigLocation";

    private ZYApplicationContext context;

    private List<ZYHandlerMapping> handlerMappings = new ArrayList<ZYHandlerMapping>();

    private Map<ZYHandlerMapping,ZYHandlerAdapter> handlerAdapters = new HashMap<ZYHandlerMapping, ZYHandlerAdapter>();

    private List<ZYViewResolver> viewResolvers = new ArrayList<ZYViewResolver>();

    @Override
    protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
        this.doPost(req, resp);
    }

    @Override
    protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
        try {
            this.doDispatcher(req,resp);
        } catch (Exception e) {
            e.printStackTrace();
        }
    }

    @Override
    public void init(ServletConfig config) throws ServletException {
        //1.初始化Application
        context = new ZYApplicationContext(config.getInitParameter(CONTEXT_CONFIG_LOCATION));
        //2.初始化九大组件
        initStrategies(context);
    }

    /**
     * Initialize the strategy objects that this servlet uses.
     * <p>May be overridden in subclasses in order to initialize further strategy objects.
     */
    //初始化策略
    protected void initStrategies(ZYApplicationContext context) {
        //多文件上传的组件
        initMultipartResolver(context);
        //初始化本地语言环境
        initLocaleResolver(context);
        //初始化模板处理器
        initThemeResolver(context);

        //handlerMapping，必须实现
        initHandlerMappings(context);
        //初始化参数适配器，必须实现
        initHandlerAdapters(context);

        //初始化异常拦截器
        initHandlerExceptionResolvers(context);
        //初始化视图预处理器
        initRequestToViewNameTranslator(context);

        //初始化视图转换器，必须实现
        initViewResolvers(context);

        //初始化重定向页面参数管理器
        initFlashMapManager(context);
    }

    private void initFlashMapManager(ZYApplicationContext context) {

    }

    private void initViewResolvers(ZYApplicationContext context) {
        //拿到模板的存放目录：appliationContext.properties配置文件中配置
        String templateRoot = context.getConfig().getProperty("template.root");
        String templateRootPath = this.getClass().getClassLoader().getResource(templateRoot).getFile();

        File templateRootDir = new File(templateRootPath);
        String[] templates = templateRootDir.list();
        for (int i = 0; i < templates.length; i ++) {
            //这里主要是为了兼容多模板，所有模仿Spring用List保存
            //在我写的代码中简化了，其实只有需要一个模板就可以搞定
            //只是为了仿真，所有还是搞了个List
            this.viewResolvers.add(new ZYViewResolver(templateRoot));
        }

    }

    private void initRequestToViewNameTranslator(ZYApplicationContext context) {

    }

    private void initHandlerExceptionResolvers(ZYApplicationContext context) {

    }

    private void initHandlerAdapters(ZYApplicationContext context) {
        //把一个requet请求变成一个handler，参数都是字符串的，自动配到handler中的形参

        //可想而知，他要拿到HandlerMapping才能干活
        //就意味着，有几个HandlerMapping就有几个HandlerAdapter
        for(ZYHandlerMapping handlerMapping:handlerMappings){
            this.handlerAdapters.put(handlerMapping,new ZYHandlerAdapter());
        }

    }

    private void initHandlerMappings(ZYApplicationContext context) {
        String[] beanNames = context.getBeanDefinitionNames();

        for(String beanName : beanNames){
            Object controller = context.getBean(beanName);

            Class<?> clazz = controller.getClass();
            if(!clazz.isAnnotationPresent(ZYController.class)){
                continue;
            }

            String baseUrl = "";
            //获取Controller的url配置
            if(clazz.isAnnotationPresent(ZYRequestMapping.class)){
                ZYRequestMapping zyRequestMapping = clazz.getAnnotation(ZYRequestMapping.class);
                baseUrl = zyRequestMapping.value();
            }

            Method[] methods = clazz.getMethods();
            for(Method method: methods){
                //没有加RequestMapping注解的直接忽略
                if(!method.isAnnotationPresent(ZYRequestMapping.class)){
                    continue;
                }

                //映射URL
                ZYRequestMapping requestMapping = method.getAnnotation(ZYRequestMapping.class);
                //  /demo/query

                //  (//demo//query)

                String regex = ("/" + baseUrl + "/" + requestMapping.value().replaceAll("\\*",".*")).
                    replaceAll("/+", "/");
                Pattern pattern = Pattern.compile(regex);

                this.handlerMappings.add(new ZYHandlerMapping(pattern,controller,method));
                System.out.println("Mapped " + regex + "," + method);
            }
        }

    }

    private void initThemeResolver(ZYApplicationContext context) {

    }

    private void initLocaleResolver(ZYApplicationContext context) {
    }

    private void initMultipartResolver(ZYApplicationContext context) {
    }

    private void doDispatcher(HttpServletRequest req, HttpServletResponse resp) throws Exception {
        //1、通过从request中拿到URL，去匹配一个HandlerMapping
        ZYHandlerMapping handler = getHandler(req);

        if(handler == null){
            processDispatchResult(req,resp,new ZYModelAndView("404"));
            return;
        }

        //2、准备调用前的参数
        ZYHandlerAdapter ha = getHandlerAdapter(handler);

        //3、真正的调用方法,返回ModelAndView存储了要传页面上值，和页面模板的名称
        ZYModelAndView mv = ha.handle(req,resp,handler);

        //这一步才是真正的输出
        processDispatchResult(req, resp, mv);

    }

    private ZYHandlerMapping getHandler(HttpServletRequest req) throws Exception{
        if(this.handlerMappings.isEmpty()){ return null; }

        String url = req.getRequestURI();
        String contextPath = req.getContextPath();
        url = url.replace(contextPath, "").replaceAll("/+", "/");

        for (ZYHandlerMapping handler : this.handlerMappings) {
            try{
                Matcher matcher = handler.getPattern().matcher(url);
                //如果没有匹配上继续下一个匹配
                if(!matcher.matches()){ continue; }

                return handler;
            }catch(Exception e){
                throw e;
            }
        }
        return null;
    }

    private ZYHandlerAdapter getHandlerAdapter(ZYHandlerMapping handler) {
        if(this.handlerAdapters.isEmpty()){return null;}
        ZYHandlerAdapter ha = this.handlerAdapters.get(handler);
        if(ha.supports(handler)){
            return ha;
        }
        return null;
    }

    private void processDispatchResult(HttpServletRequest req, HttpServletResponse resp, ZYModelAndView mv) throws Exception{
        //把给我的ModleAndView变成一个HTML、OuputStream、json、freemark、veolcity
        //ContextType
        if(null == mv){return;}

        //如果ModelAndView不为null，怎么办？
        if(this.viewResolvers.isEmpty()){return;}

        for (ZYViewResolver viewResolver : this.viewResolvers) {
            ZYView view = viewResolver.resolveViewName(mv.getViewName(),null);
            view.render(mv.getModel(),req,resp);
            return;
        }


    }
}
